﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace Pong
{
    public partial class MainPage : UserControl
    {
        bool slowComputer = true; //TODO: should be calculated somehow or set by the user
        //A ZoomBlur effect covering the whole gameboard.
        ZoomBlurEffect blurZoom = new ZoomBlurEffect();
        CompositionTargetGameLoop gameLoop;
        KeyboardState keyboardState = new KeyboardState();
        Pawn player1;
        Pawn player2;
        Pawn ball;
        Pawn gameboard;
        const double maxPlayerVelocity = 16.0;
        const double maxBallVelocity = 16.0;
        const double initialBallVelocity = 8.0;
        //How much speed does the player gain for each tick that he travels in the same direction
        const double acceleratePlayerIncrement = 0.5;
        Random random = new Random();

        public MainPage()
        {
            InitializeComponent();
            if (!slowComputer)
            {
                //Enable blurZoom effect.  Old computers can't handle it, the framerate becomes unacceptable.
                rectGameBoard.Effect = blurZoom;
            }
            // Wire events
            this.KeyDown += new KeyEventHandler(Page_KeyDown);
            this.KeyUp += new KeyEventHandler(Page_KeyUp);
            //this.MouseEnter += new MouseEventHandler(Page_MouseEnter);
            //this.MouseLeave += new MouseEventHandler(Page_MouseLeave);            
            //this.MouseLeftButtonUp += new MouseButtonEventHandler(Page_MouseLeftButtonUp);
            //this.MouseMove += new MouseEventHandler(Page_MouseMove);
            this.GotFocus += new RoutedEventHandler(Page_GotFocus);
            this.LostFocus += new RoutedEventHandler(Page_LostFocus);
            this.MouseLeftButtonDown += new MouseButtonEventHandler(Page_MouseLeftButtonDown);

            // Create pawns
            player1 = new Pawn(rectPlayer1);
            player2 = new Pawn(rectPlayer2);
            ball = new Pawn(rectBall);
            gameboard = new Pawn(rectGameBoard);

            // Set up gameloop and initial game conditions
            gameLoop = new CompositionTargetGameLoop();
            gameLoop.Update += new GameLoop.UpdateHandler(gameLoop_Update);
            this.Start();
        }

        private double PlusOrMinus()
        {
            if (random.NextDouble() > 0.5)
                return 1.0;
            else
                return -1.0;
        }

        public void Start()
        {
            var yPos = (rectGameBoard.Height - rectPlayer1.Height) / 2.0;

            // Set initial position and transformation of player 1
            player1.SetPosition(0, yPos);

            // Set initial position and transformation of player 2
            player2.SetPosition(rectGameBoard.Width - rectPlayer2.Width, yPos);

            // Set initial position and transformation of ball            
            ball.SetPosition((rectGameBoard.Width - rectBall.Width) / 2.0, (rectGameBoard.Height - rectBall.Height) / 2.0);

            // Let the ball be orange!
            ball.PawnDisplayer.Fill = new SolidColorBrush(Color.FromArgb(255, 255, 200, 0));

            // Give the ball a random starting velocity at a fixed speed
            ball.Velocity.X = initialBallVelocity * PlusOrMinus();
            ball.Velocity.Y = initialBallVelocity * PlusOrMinus();

            blurZoom.Center = new Point(0, 0);
            UpdateScore();

        }

        int[] score = { 0, 0 };
        private void UpdateScore(int player)
        {
            score[player - 1] += 1;
            UpdateScore();
        }

        private void UpdateScore()
        {
            // Set player status text
            txtPlayer1Score.Text = "Player 1 : " + score[0];
            txtPlayer2Score.Text = "Player 2 : " + score[1];
        }

        private double ticks = 0.0;

        void gameLoop_Update(object sender, TimeSpan elapsed)
        {
            // Fuck around with the ball shader blur as function of position x from center of map
            BallBlurEffect.Radius = 25.0 * Math.Abs(ball.X - rectGameBoard.Width / 2.0) / rectGameBoard.Width;
            HandlePlayerMovement();
            MoveBall();

            // Fuck around with the awesome zoomblur thingy so its center of focus is the ball!
            var center = new Point();
            center.X = ball.X / rectGameBoard.Width;
            center.Y = ball.Y / rectGameBoard.Height;
            blurZoom.Center = center;

            // Let the blur amount be a function of time
            ticks += 0.01;
            ticks %= 360.0;
            blurZoom.BlurAmount = 1.0 + Math.Sin(ticks);
        }

        private Color RandomColor()
        {
            int a = random.Next(150, 255);
            int r = random.Next(220, 255);
            int g = random.Next(180, r);
            int b = random.Next(40);
            return Color.FromArgb((Byte)a, (Byte)r, (Byte)g, (Byte)b);
        }

        private void PlaySound(String soundName)
        {
            var sound = ((MediaElement)this.layoutRoot.FindName(soundName));
            sound.Stop();
            // Enable stereo panning for all sounds except the music!
            if (soundName != "music")
            {
                sound.Balance = ((ball.X / rectGameBoard.Width) * 2.0) - 1.0;
            }
            sound.Play();
        }

        private void MoveBall()
        {
            // Update ball velocity with regards to collision
            // TODO: Need to get collision results, not just bool.
            // For instance, let the ball hit under the player pawn,
            // it will bounce inside the player and then be ejected
            // when it should instead reverse it's Y velocity.
            CollisionResult p1R = CollisionDetector.RectInRect(ball, player1);
            CollisionResult p2R = CollisionDetector.RectInRect(ball, player2);

            // Check if ball collides with player 1
            if (p1R != CollisionResult.NONE)
            {
                player1.PawnDisplayer.Fill = new SolidColorBrush(RandomColor());
                PlaySound("ballHitPlayer");
                if (p1R == CollisionResult.BOTTOM || p1R == CollisionResult.TOP)
                {
                    ball.Velocity.Y *= -1;
                }
                else
                {
                    ball.Velocity.X *= -1;
                }
            }
            // Check if ball collides with player 2
            else if (p2R != CollisionResult.NONE)
            {
                player2.PawnDisplayer.Fill = new SolidColorBrush(RandomColor());
                PlaySound("ballHitPlayer");
                if (p2R == CollisionResult.BOTTOM || p2R == CollisionResult.TOP)
                {
                    ball.Velocity.Y *= -1;
                }
                else
                {
                    ball.Velocity.X *= -1;
                }
            }
            else
            {
                // Check ball and gameboard
                CollideCheckBallGameBoard();
            }
            // Finally, move the ball
            ball.X += ball.Velocity.X;
            ball.Y += ball.Velocity.Y;


        }

        private void CollideCheckBallGameBoard()
        {
            // Check if ball collides with map

            // TODO: Refactor with collision detector

            // Handle X
            if ((ball.X >= rectGameBoard.Width && ball.Velocity.X > 0.0) || (ball.X <= 0.0 && ball.Velocity.X < 0.0))
            {
                PlaySound("ballHitWall");

                if (ball.Velocity.X > 0)
                    UpdateScore(1);
                else
                    UpdateScore(2);

                // Increase the speed, the game gets harder with every score!
                if (Math.Abs(ball.Velocity.X) < maxBallVelocity)
                {
                    ball.Velocity.X *= -1.10;
                    ball.Velocity.Y *= 1.1;
                }
            }

            // Handle Y
            if ((ball.Y >= rectGameBoard.Height && ball.Velocity.Y > 0.0) || (ball.Y <= 0.0 && ball.Velocity.Y < 0.0))
            {
                PlaySound("ballHitFloor");
                ball.Velocity.Y *= -1.0;
                //ball.Velocity.X += random.NextDouble() / 2.0;
            }
        }

        private void HandlePlayerMovement()
        {
            // Player 1
            if (keyboardState[Key.A])
            {
                MovePlayerUp(player1);
            }
            if (keyboardState[Key.Z])
            {
                MovePlayerDown(player1);
            }

            // Player 2
            if (keyboardState[Key.Up])
            {
                MovePlayerUp(player2);
            }
            if (keyboardState[Key.Down])
            {
                MovePlayerDown(player2);
            }
        }

        private void MovePlayerDown(Pawn player)
        {
            MovePlayer(player, 1);
        }

        private void MovePlayerUp(Pawn player)
        {
            MovePlayer(player, -1);
        }

        private void MovePlayer(Pawn player, int sign)
        {
            CalculatePlayerVelocity(player, sign);
            CalculatePlayerPosition(player);
        }

        private static void CalculatePlayerVelocity(Pawn player, int sign)
        {
            // No inertia if player is being reversed
            if (Math.Sign(player.Velocity.Y) != sign)
            {
                player.Velocity.Y = 0;
            }

            // Just increase velocity if maximum speed has not been reached
            if (Math.Abs(player.Velocity.Y) < maxPlayerVelocity)
            {
                player.Velocity.Y += sign * acceleratePlayerIncrement;
            }
        }

        private void CalculatePlayerPosition(Pawn player)
        {
            player.Y += player.Velocity.Y;

            if (player.Y < 0)
            {
                // If the player is crashing into the ceiling, back of a bit.
                player.Y = 0;
            } 
            else if (player.Y + player.PawnDisplayer.ActualHeight > gameboard.PawnDisplayer.ActualHeight)
            {
                // If the player is crashing into the floor, back of a bit.
                player.Y = gameboard.PawnDisplayer.ActualHeight - player.PawnDisplayer.ActualHeight;
            }
        }

        void Page_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            blurZoom.BlurAmount = 0.5;
            PlaySound("music");
            gameLoop.Start();
        }

        void Page_LostFocus(object sender, RoutedEventArgs e)
        {
            gameLoop.Stop();
        }

        void Page_GotFocus(object sender, RoutedEventArgs e)
        {
            gameLoop.Start();
        }

        void Page_KeyDown(object sender, KeyEventArgs e)
        {
            keyboardState[e.Key] = true;
        }

        void Page_KeyUp(object sender, KeyEventArgs e)
        {
            keyboardState[e.Key] = false;
        }

        void Page_MouseEnter(object sender, MouseEventArgs e)
        {

        }

        void Page_MouseLeave(object sender, MouseEventArgs e)
        {

        }

        void Page_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {

        }

        void Page_MouseMove(object sender, MouseEventArgs e)
        {

        }
    }
}
